package com.niuwa.p2p.core.finance.repay;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.niuwa.p2p.core.constant.Constants;
import com.niuwa.p2p.core.finance.CostCalculatorHelper;
import com.niuwa.p2p.core.utils.DateUtil;
import com.niuwa.p2p.core.utils.DictConstant;
import com.niuwa.p2p.core.utils.ReflectUtil;
import com.niuwa.p2p.entity.business.RepayPlan;

/**
 * 还款 
 * 1.实现checkInputVo,确保每个借款项目信息存在
 * 2.实现getRepayShould,算出当期应该还款(正常还款) 
 * 
 * @title Repay.java
 *
 * @author 计盛君
 * @version 1.0
 * @created 2015年4月29日
 */
public class Repay extends AbstractRepay{

	/**---------------------------------------------inputVocheck-----------------------------------------------------------------**/
	public boolean checkInputVo(LoanInfoVo loanInfo){
		try {
			return ReflectUtil.ObjectHasNoEmptyValue(loanInfo);
		} catch (Exception e) {
			return false;
		}
	}
	
	/**---------------------------------------------还款计划作成----------------------------------------------------------------------
	 * @throws Exception **/	
	@Override
	public List<RepayPlan> createRepayPlan(LoanInfoVo loanInfoVo) throws Exception {
		// inputVo项目出错,直接返回
		if(this.checkInputVo(loanInfoVo) == false){
			return null;
		}
		// 还款计划做成
		List<RepayPlan> repayList = new ArrayList<RepayPlan>();
		// TODO DICT 待定
		if(loanInfoVo.getRepayMethod().equals("ACPI")&&loanInfoVo.getRepayTermType().equals("M")){//等额本息(按月)
			ACPI(loanInfoVo, repayList);
		} else if(loanInfoVo.getRepayMethod().equals("FISC")&&loanInfoVo.getRepayTermType().equals("M")){//先息后本(按月)
			IRAF(loanInfoVo, repayList);
		} else if(loanInfoVo.getRepayMethod().equals("LCI")){//一次还清(按月、按日)
			ONETERM(loanInfoVo, repayList);
		}
//		// 取得的还款计划异常,直接返回
//		if(repayList.size() == 0 || repayList.size() > getMonthTermByLoanInfoVo(loanInfoVo)) {
//			return null;
//		}
		return repayList;
	}
	
	/**
	 * 等额本息计算
	 * @param loanInfoVo 借款信息
	 * @param RepayPlanList 还款计划
	 * @return
	 * @throws Exception 
	 */
	public boolean ACPI(LoanInfoVo loanInfoVo, List<RepayPlan> repayPlanList) throws Exception{	
				
		return ACPI(loanInfoVo.getRepayTotalAmount(), getMonthRateByLoanInfoVo(loanInfoVo), getMonthTermByLoanInfoVo(loanInfoVo), 
				loanInfoVo.getLoanDate(), loanInfoVo.getPlatformFeeMethod(), loanInfoVo.getPlatformFeeRate(), repayPlanList);
	}
	
	/**
	 * 取得借款月利率
	 * 
	 * @param loanInfoVo
	 * @return
	 */
	private BigDecimal getMonthRateByLoanInfoVo(LoanInfoVo loanInfoVo){
		BigDecimal monthRate = loanInfoVo.getRepayRate();
		// DICT TODO
		if(loanInfoVo.getRateType().equals("年")){
			monthRate = CostCalculatorHelper.rateConvert(loanInfoVo.getRepayRate(), "YEART_TO_MONTH");
		}
		return monthRate;
	}
	
	/**
	 * 取得借款日利率
	 * 
	 * @param loanInfoVo
	 * @return
	 */
	private BigDecimal getDailyRateByLoanInfoVo(LoanInfoVo loanInfoVo){
		BigDecimal monthRate = loanInfoVo.getRepayRate();
		// DICT TODO
		if(loanInfoVo.getRateType().equals("年")){
			monthRate = CostCalculatorHelper.rateConvert(loanInfoVo.getRepayRate(), "YEAR_TO_DAY");
		} else if(loanInfoVo.getRateType().equals("月")){
			monthRate = CostCalculatorHelper.rateConvert(loanInfoVo.getRepayRate(), "MONTH_TO_DAY");
		}
		return monthRate;
	}
	
		
	/**
	 * 取得借款月期限
	 * 
	 * @param loanInfoVo 
	 * @return
	 * @throws Exception 
	 */
	private int getMonthTermByLoanInfoVo(LoanInfoVo loanInfoVo) throws Exception{
		int monthTerm = loanInfoVo.getRepayTerm();
		// DICT TODO
		if(!loanInfoVo.getRepayTermType().equals("M")){
			// 业务错误，业务规定借款月期限只允许出现：月 ，天
			throw new Exception();
		}
		return monthTerm;
	}
		
	/**
	 * 取得借款日期限
	 * 
	 * @param loanInfoVo 
	 * @return
	 * @throws Exception 
	 */
	private int getDailyTermByLoanInfoVo(LoanInfoVo loanInfoVo) throws Exception{
		int dailyTerm = loanInfoVo.getRepayTerm();
		// DICT TODO
		if(!loanInfoVo.getRepayTermType().equals("D")){
			// 业务错误，业务规定借款月期限只允许出现：月 ，天
			throw new Exception();
		}		
		return dailyTerm;
	}	
	
	/**
	 * 等额本息计算
	 * average capital plus interest
	 * 计算公式:     AB(1+β)m次方
	 *       x = -------------
	 *            (1+β)m次方 - 1
	 * x:月还款额           
	 * A:贷款总额为A
	 * β:银行月利率为β
	 * m:总期数为m（个月）
	 *  
	 * @param repayTotalAmout 还款总额
	 * @param monthRate 月利率
	 * @param monthTerm 总还款期数(月)
	 * @param loanDate 借款日期
	 * @param platformFeeMethod 平台服务费收取方式: 1.一次性预收 2.每月收取
	 * @param platformFeeRate 平台服务费率
	 * @param RepayPlanList 还款计划
	 * @return 
	 */
	public boolean ACPI(BigDecimal repayTotalAmout, BigDecimal monthRate, int monthTerm, Date loanDate,
			String platformFeeMethod, BigDecimal platformFeeRate, List<RepayPlan> repayPlanList){
		
		// (1+β)m次方 
		BigDecimal tmp1 = (monthRate.add(new BigDecimal(1))).pow(monthTerm);
		// 除数
		BigDecimal tmp2 = repayTotalAmout.multiply(monthRate).multiply(tmp1);
		// 被除数
		BigDecimal tmp3 = tmp1.subtract(new BigDecimal(1));
		/** 
		 * 每月定额还款 
		 * 精确位数:小数点以后20位
		 * ROUND:
		 * ROUND_HALF_UP     : 遇到.5的情况时往上近似,例: 1.5 ->;2
		 * ROUND_HALF_DOWN   : 遇到.5的情况时往下近似,例: 1.5 ->;1
		 * ROUND_CEILING     : 如果   BigDecimal   是正的，则做   ROUND_UP 操作；如果为负，则做   ROUND_DOWN操作。    
		 * ROUND_DOWN        : 从不在舍弃(即截断)的小数之前增加数字。    
		 * ROUND_FLOOR       : 如果   BigDecimal   为正，则作   ROUND_UP；如果为负，则作   ROUND_DOWN。    
		 * ROUND_HALF_DOWN   : 若舍弃部分>.5，则作   ROUND_UP；否则，作   ROUND_DOWN。    
		 * ROUND_HALF_EVEN   : 如果舍弃部分左边的数字为奇数，则作   ROUND_HALF_UP；如果它为偶数，则作   ROUND_HALF_DOWN。    
		 * ROUND_HALF_UP     : 若舍弃部分>=.5，则作   ROUND_UP；否则，作   ROUND_DOWN   。    
		 * ROUND_UNNECESSARY : 该"伪舍入模式"实际是指明所要求的操作必须是精确的，，因此不需要舍入操作。    
		 * ROUND_UP          : 总是在非   0   舍弃小数(即截断)之前增加数字。  
		 */
		int scale = Constants.CURRENCY_CALCULATE_SCALE;
		BigDecimal repayAmout = tmp2.divide(tmp3, scale, BigDecimal.ROUND_HALF_UP);
	    // 当月利息
		BigDecimal currentRepayInterest = BigDecimal.ZERO;
	    // 当月本金
		BigDecimal currentRepayCapital = BigDecimal.ZERO ;
	    // 当月剩余本金 (初始值:还款总额)
		BigDecimal currentRemainRepayCapital = repayTotalAmout;
		//TODO DICT
		boolean  isAveragePlatformFee = platformFeeMethod.equals("每月收取");
		BigDecimal averagedPlatformFee = BigDecimal.ZERO ;
    	if(isAveragePlatformFee) {
    		averagedPlatformFee = CostCalculatorHelper.averagePlatformFee(repayTotalAmout, platformFeeRate, monthTerm); 		
    	}
		
		// 计算每月还款信息
	    for(int i = 1; i <=monthTerm; i++){
	    	// 当月利息 = 当月剩余本金 * 月利率
	    	currentRepayInterest = currentRemainRepayCapital.multiply(monthRate);
	    	// 当月本金 = 每月定额还款  - 当月利息;
	    	currentRepayCapital = repayAmout.subtract(currentRepayInterest);
	    	// 当月剩余本金 = 还款总额 - 当月本金 
	    	currentRemainRepayCapital = currentRemainRepayCapital.subtract(currentRepayCapital);
	    	// 设定还款信息
	    	RepayPlan repayPlan = new RepayPlan();
	    	repayPlan.setTermNo(i);
	    	repayPlan.setRepayInterest(currentRepayInterest.setScale(2, BigDecimal.ROUND_HALF_UP));
	    	repayPlan.setRepayCapital(currentRepayCapital.setScale(2, BigDecimal.ROUND_HALF_UP));
	    	repayPlan.setRepayDate(DateUtil.addMonths(loanDate, i));	    	
	    	if(isAveragePlatformFee) {
	    		repayPlan.setPlatformFee(averagedPlatformFee.setScale(2, BigDecimal.ROUND_HALF_UP)); 		
	    	} 
	    	repayPlan.setRepayAmount(repayAmout.add(averagedPlatformFee).setScale(2, BigDecimal.ROUND_HALF_UP));
	    	repayPlanList.add(repayPlan);
	    }
	    return true;
	}
	
	/**
	 * 先息后本计算
	 * @param loanInfoVo 借款信息
	 * @param RepayPlanList 还款计划
	 * @return
	 * @throws Exception 
	 */
	public boolean IRAF(LoanInfoVo loanInfoVo, List<RepayPlan> RepayPlanList) throws Exception{								
		return IRAF(loanInfoVo.getRepayTotalAmount(), getMonthRateByLoanInfoVo(loanInfoVo), getMonthTermByLoanInfoVo(loanInfoVo), 
				loanInfoVo.getLoanDate(), loanInfoVo.getPlatformFeeMethod(), loanInfoVo.getPlatformFeeRate(), RepayPlanList);
	}

	/**
	 * 先息后本计算
	 * The interest rate after the first
	 * 每月利率相同，最有一期归还本金
	 * 每期月利息 = 贷款总额为  * 月利率
	 *  
	 * @param repayTotalAmout 还款总额
	 * @param monthRate 月利率
	 * @param monthTerm 总还款期数(月)
	 * @param loanDate 借款日期
	 * @param platformFeeMethod 平台服务费收取方式: 1.一次性预收 2.每月收取
	 * @param platformFeeRate 平台服务费率
	 * @param RepayPlanList 还款计划
	 * @return 
	 */
	public boolean IRAF(BigDecimal repayTotalAmout, BigDecimal monthRate, int monthTerm, Date loanDate,
			String platformFeeMethod, BigDecimal platformFeeRate, List<RepayPlan> repayPlanList){
		
		// 当月利息
		BigDecimal currentRepayInterest = repayTotalAmout.multiply(monthRate);
	    // 当月本金
		BigDecimal currentRepayCapital = BigDecimal.ZERO ;
	    // 当月还款
		BigDecimal currentRepayAmout = currentRepayInterest;
		
		boolean  isAveragePlatformFee = platformFeeMethod.equals("每月收取");
		BigDecimal averagedPlatformFee = BigDecimal.ZERO ;
    	if(isAveragePlatformFee) {
    		averagedPlatformFee = CostCalculatorHelper.averagePlatformFee(repayTotalAmout, platformFeeRate, monthTerm); 		
    	}		
		// 计算每月还款信息
	    for(int i = 1; i <= monthTerm; i++){
	    	// 设定还款信息
	    	RepayPlan repayPlan = new RepayPlan();
	    	repayPlan.setTermNo(i);
	    	repayPlan.setRepayInterest(currentRepayInterest.setScale(2, BigDecimal.ROUND_HALF_UP));
	    	if(isAveragePlatformFee) {
	    		repayPlan.setPlatformFee(averagedPlatformFee.setScale(2, BigDecimal.ROUND_HALF_UP)); 
	    	}
	    	if(i == monthTerm){
	    		// 最后一期：当月本金= 总金
	    		currentRepayCapital = repayTotalAmout;	    		
	    	   	// 最后一期：当月还款  = 总金额  + 当月利息
	    		currentRepayAmout = currentRepayCapital.add(currentRepayInterest);
	    	}
	    	repayPlan.setRepayCapital(currentRepayCapital.setScale(2, BigDecimal.ROUND_HALF_UP));
	    	repayPlan.setRepayDate(DateUtil.addMonths(loanDate, i));
	    	repayPlan.setRepayAmount(currentRepayAmout.add(averagedPlatformFee).setScale(2, BigDecimal.ROUND_HALF_UP));
	    	repayPlanList.add(repayPlan);
	    }
	    return true;
	}
		
	/**
	 * 先息后本计算 一次还清(APP用)
	 * @param loanInfoVo 借款信息
	 * @param RepayPlanList 还款计划
	 * @return
	 * @throws Exception 
	 */
	public boolean ONETERM(LoanInfoVo loanInfoVo, List<RepayPlan> RepayPlanList) throws Exception{								
		return ONETERM(loanInfoVo.getRepayTotalAmount(), loanInfoVo.getRepayRate(), loanInfoVo.getRepayTerm(), 
				loanInfoVo.getLoanDate(), loanInfoVo.getPlatformFeeMethod(), loanInfoVo.getPlatformFeeRate(),loanInfoVo.getCustId(),loanInfoVo.getProjectNo(),loanInfoVo.getRepayTermType(), RepayPlanList);
	}
	
	/**
	 * 一次还清(只有1期,
	 * The interest rate after the first
	 * 每月利率相同，最有一期归还本金
	 * 每期月利息 = 贷款总额为  * 月利率
	 *  
	 * @param repayTotalAmout 还款总额
	 * @param dayRate 日利率
	 * @param term 还款期限：天，月
	 * @param loanDate 借款日期
	 * @param platformFeeMethod 平台服务费收取方式: 1.一次性预收 2.每月收取
	 * @param platformFeeRate 平台服务费率
	 * @param RepayPlanList 还款计划
	 * @return 
	 */
	public boolean ONETERM(BigDecimal repayTotalAmout, BigDecimal yearRate, int term, Date loanDate,
			String platformFeeMethod, BigDecimal platformFeeRate,Long custId,String projectNo,String repayTermType, List<RepayPlan> repayPlanList){
		// 设定还款信息
		RepayPlan repayPlan = new RepayPlan();
		BigDecimal averagedPlatformFee = CostCalculatorHelper.averagePlatformFee(repayTotalAmout, platformFeeRate, 1);
		// 当月本金
		BigDecimal currentRepayCapital = repayTotalAmout;
		repayPlan.setRepayCapital(currentRepayCapital);
		if(repayTermType.equals("M")){
			BigDecimal interest = CostCalculatorHelper.disposableInterest(repayTotalAmout, yearRate, new BigDecimal(term));
			repayPlan.setRepayAmount(repayTotalAmout.add(interest).add(averagedPlatformFee).setScale(2, BigDecimal.ROUND_HALF_UP));
			repayPlan.setRepayInterest(interest);
			Date d = DateUtil.getNextMonthTime(term, DictConstant.TimeFormat.SHORTTIME.getCode());
			repayPlan.setRepayDate(DateUtil.getNextDayTime(-1, DictConstant.TimeFormat.SHORTTIME.getCode(), d));
			System.out.println(repayPlan.getRepayDate());
		} else if(repayTermType.equals("D")){
			// 当月利息
			BigDecimal currentRepayInterest = CostCalculatorHelper.dayInterest(term, repayTotalAmout, yearRate.multiply(new BigDecimal("100")));
			// 当月还款
			BigDecimal currentRepayAmout = currentRepayCapital.add(currentRepayInterest);
			repayPlan.setRepayAmount(currentRepayAmout.add(averagedPlatformFee).setScale(2, BigDecimal.ROUND_HALF_UP));
			repayPlan.setRepayInterest(currentRepayInterest.setScale(2, BigDecimal.ROUND_HALF_UP));
			repayPlan.setRepayDate(DateUtil.getNextDayTime(term-1, DictConstant.TimeFormat.SHORTTIME.getCode(), new Date()));
		} else {
			new Exception("类型异常");
		}
    	repayPlan.setTermNo(1);
    	repayPlan.setPlatformFee(averagedPlatformFee.setScale(2, BigDecimal.ROUND_HALF_UP));
    	repayPlan.setRepayStatus(DictConstant.REPAY_STATUS.WAIT_REPAY.getCode());
    	repayPlan.setDelFlag("N");
    	repayPlan.setIsAdvance("N");
    	repayPlan.setProjectNo(projectNo);
    	repayPlan.setRepaymentId(custId); //custI
    	repayPlanList.add(repayPlan);	
	    return true;
	}
	/**---------------------------------------------还款金额计算----------------------------------------------------------------------**/
	
	/** 
	 * 根据已经存入DB的当期还款信息和还款日，结合借款信息（合同本金，借款方式等）计算费率，算出应还金额
	 * 
	 * 正常还款，偿还当期的金额
	 * 
	 * @param loanInfo 借款信息
	 * @param RepayPlan   当期还款信息
	 * @param repaymentDay 还款日
	 * @return 还款金额
	 */
	public BigDecimal getRepayShould(LoanInfoVo loanInfoVo, RepayPlan repayPlan, Date repaymentDay, IRepayCallback repayCallback) throws Exception{
		
		// 正常还款金额
		if(DateUtil.isSameDay(repaymentDay, repayPlan.getRepayDate()) == true) {
			// 正常还款：还款日 = 计划还款日
			return repayPlan.getRepayAmount();
		} else if(DateUtil.isAfter(repaymentDay, DateUtil.addMonths(repayPlan.getRepayDate(), -1))
				&& DateUtil.isBefore(repaymentDay, repayPlan.getRepayDate())) {
			BigDecimal repayShouldDaily = BigDecimal.ZERO;
			int overdueDays = DateUtil.getDaysBetween(repayPlan.getRepayDate(), repaymentDay);
			// TODO DICT 待定
			if(loanInfoVo.getRepayMethod().equals("等额本息")){
				// 提前还款： 前月计划还款日 < 还款日 < 当月计划还款日
				// 提前还款：正常还款金额 = 当月本金  + 日利息
				List<RepayPlan> repayPlanList = createRepayPlan(loanInfoVo);
				// 日利息=上期剩余本金*日利率*逾期天数 (利用超期利息方式计算,由于相同)
				repayShouldDaily = repayPlan.getRepayCapital().add(CostCalculatorHelper.overdueInterest(getPreRemainRepayCapital(repayPlanList, repayPlan), getDailyRateByLoanInfoVo(loanInfoVo), overdueDays));											
			} else if(loanInfoVo.getRepayMethod().equals("先息后本")){
				// 提前还款： 前月计划还款日 < 还款日 < 当月计划还款日
				// 提前还款：正常还款金额 = 当月本金  + 日利息
				// 日利息=总金额*日利率*逾期天数 (利用超期利息方式计算,由于相同)
				repayShouldDaily = repayPlan.getRepayCapital().add(CostCalculatorHelper.overdueInterest(loanInfoVo.getRepayTotalAmount(), getDailyRateByLoanInfoVo(loanInfoVo), overdueDays));
			}			
			return repayShouldDaily;
		} else {
			// 抛出 错误（业务逻辑错误，不是正常还款）
			throw new Exception();
		}
	}
		
	/**
	 * 取得上期剩余本金
	 * 
	 * @param loanInfo 借款信息
	 * @param RepayPlan 当期还款信息
	 */
	private BigDecimal getPreRemainRepayCapital(List<RepayPlan> repayPlanList, RepayPlan repayPlan){ 
		return repayPlanList.get(repayPlan.getTermNo() - 1 - 1).getRepayCapital();
	}
		
	/**
	 * 是否清偿结束
	 * 1.每天的Batch(夜间-凌晨)处理会把下次的应还金额算出入库
	 * 2.是否清偿结束 = 实还金额   == 应还金额 
	 * 3.提前还款的逾期费用,即可利用Batch跑出的结果,不需重新计算
	 * 
	 * @param RepayPlan
	 * @return 0:全部偿还结清   >0:没有结束
	 */
	protected BigDecimal getCurrentRepayShouldRemainAmout(com.niuwa.p2p.entity.business.RepayPlan repayPlan) {
		// 当期剩余应还金额 
		BigDecimal repayShouldRemainAmout = BigDecimal.ZERO;
		// 判断是否清偿结束
		if(repayPlan.getRepayAmount() == null || repayPlan.getRepayAmount().compareTo(BigDecimal.ZERO) == 0){
			// 重未清偿,视为 逾期
			return repayShouldRemainAmout;
		}else if(repayPlan.getRepayAmount().compareTo(BigDecimal.ZERO) == 1){
			// 实际还款日 > 0 && 实还金额   == 应还金额 (当期该偿还的逾期总额)
			// 当期剩余应还金额  = 应还金额  - 实还金额
			repayShouldRemainAmout = repayPlan.getRepayAmount().subtract(repayPlan.getActRepayAmount());
		}
		return repayShouldRemainAmout;		 
	}
	
	/** 
	 * 当期是否逾期
	 * 
	 * @param RepayPlan
	 * @return true:逾期   false:不逾期
	 */
	public boolean isOverdue(com.niuwa.p2p.entity.business.RepayPlan RepayPlan){
		return !(getCurrentRepayShouldRemainAmout(RepayPlan).compareTo(BigDecimal.ZERO) == 0);
	}
	
	
}
